﻿namespace System.ServiceModel.DomainServices.Client
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Reflection;
    using System.ServiceModel.DomainServices;

    internal static class ObjectStateUtility
    {
        internal static void ApplyState(object o, IDictionary<string, object> stateToApply)
        {
            ApplyState(o, stateToApply, null, LoadBehavior.RefreshCurrent);
        }

        internal static void ApplyState(object o, IDictionary<string, object> stateToApply, IDictionary<string, object> originalState, LoadBehavior loadBehavior)
        {
            if (loadBehavior != LoadBehavior.KeepCurrent)
            {
                foreach (MetaMember member in MetaType.GetMetaType(o.GetType()).DataMembers)
                {
                    object obj2;
                    PropertyInfo propertyInfo = member.Member;
                    if (stateToApply.TryGetValue(propertyInfo.Name, out obj2))
                    {
                        if (((obj2 != null) && member.IsComplex) && !member.IsCollection)
                        {
                            Func<object> func = null;
                            object obj3 = propertyInfo.GetValue(o, null);
                            IDictionary<string, object> newValueState = (IDictionary<string, object>) obj2;
                            if (obj3 != null)
                            {
                                object obj4 = null;
                                if (originalState != null)
                                {
                                    originalState.TryGetValue(propertyInfo.Name, out obj4);
                                }
                                ApplyState(obj3, newValueState, (IDictionary<string, object>) obj4, loadBehavior);
                            }
                            else
                            {
                                if (func == null)
                                {
                                    func = delegate {
                                        object o = Activator.CreateInstance(propertyInfo.PropertyType);
                                        ApplyState(o, newValueState);
                                        return o;
                                    };
                                }
                                Lazy<object> lazy = new Lazy<object>(func);
                                ApplyValue(o, lazy, propertyInfo, originalState, loadBehavior);
                            }
                            continue;
                        }
                        ApplyValue(o, obj2, propertyInfo, originalState, loadBehavior);
                    }
                }
            }
        }

        private static void ApplyValue(object o, object value, PropertyInfo propertyInfo, IDictionary<string, object> originalState, LoadBehavior loadBehavior)
        {
            if (loadBehavior != LoadBehavior.KeepCurrent)
            {
                Lazy<object> lazy = value as Lazy<object>;
                if (loadBehavior == LoadBehavior.RefreshCurrent)
                {
                    if (lazy != null)
                    {
                        value = lazy.get_Value();
                    }
                    propertyInfo.SetValue(o, value, null);
                }
                else if ((loadBehavior == LoadBehavior.MergeIntoCurrent) && !PropertyHasChanged(o, originalState, propertyInfo))
                {
                    if (lazy != null)
                    {
                        value = lazy.get_Value();
                    }
                    propertyInfo.SetValue(o, value, null);
                }
            }
        }

        internal static IDictionary<string, object> ExtractRoundtripState(Type type, IDictionary<string, object> state)
        {
            MetaType metaType = MetaType.GetMetaType(type);
            Dictionary<string, object> dictionary = new Dictionary<string, object>();
            foreach (MetaMember member in metaType.DataMembers)
            {
                if (member.IsRoundtripMember)
                {
                    if (!member.IsComplex)
                    {
                        dictionary.Add(member.Member.Name, state[member.Member.Name]);
                    }
                    else
                    {
                        if (!member.IsCollection)
                        {
                            IDictionary<string, object> dictionary2 = (IDictionary<string, object>) state[member.Member.Name];
                            if (dictionary2 != null)
                            {
                                IDictionary<string, object> dictionary3 = ExtractRoundtripState(member.Member.PropertyType, dictionary2);
                                dictionary.Add(member.Member.Name, dictionary3);
                            }
                            continue;
                        }
                        IEnumerable enumerable = (IEnumerable) state[member.Member.Name];
                        if (enumerable != null)
                        {
                            Type elementType = TypeUtility.GetElementType(member.Member.PropertyType);
                            IList list = (IList) Activator.CreateInstance(typeof(List<>).MakeGenericType(new Type[] { elementType }));
                            foreach (object obj2 in enumerable)
                            {
                                IDictionary<string, object> dictionary4 = ExtractState(obj2);
                                if (dictionary4 != null)
                                {
                                    IDictionary<string, object> stateToApply = ExtractRoundtripState(elementType, dictionary4);
                                    object o = Activator.CreateInstance(elementType);
                                    ApplyState(o, stateToApply);
                                    list.Add(o);
                                }
                            }
                            dictionary.Add(member.Member.Name, list);
                        }
                    }
                }
            }
            return dictionary;
        }

        internal static IDictionary<string, object> ExtractState(object o)
        {
            return ExtractState(o, new HashSet<object>());
        }

        private static IDictionary<string, object> ExtractState(object o, HashSet<object> visited)
        {
            MetaType metaType = MetaType.GetMetaType(o.GetType());
            Dictionary<string, object> dictionary = new Dictionary<string, object>();
            if (visited.Contains(o))
            {
                throw new InvalidOperationException(Resource.CyclicReferenceError);
            }
            visited.Add(o);
            foreach (MetaMember member in metaType.DataMembers)
            {
                object obj2 = member.GetValue(o);
                if (((obj2 != null) && member.IsComplex) && !member.IsCollection)
                {
                    obj2 = ExtractState(obj2, visited);
                }
                dictionary[member.Member.Name] = obj2;
            }
            return dictionary;
        }

        internal static bool PropertyHasChanged(object o, IDictionary<string, object> originalValues, PropertyInfo prop)
        {
            object obj3;
            if (originalValues == null)
            {
                return false;
            }
            object objA = prop.GetValue(o, null);
            if (!originalValues.TryGetValue(prop.Name, out obj3))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.Entity_Property_NotChangeTracked, new object[] { prop.Name, o.GetType() }));
            }
            return !object.Equals(objA, obj3);
        }
    }
}

